home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
17 Bit Software 5: The Fifth Dimension
/
17 Bit - The Fifth Dimension (1995)(17 Bit Software)[!].iso
/
files
/
3728.dms
/
3728.adf
/
XPKDisk
/
devio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-04-08
|
28KB
|
1,138 lines
/*-
* DEVIO.C
*
* The xpkdisk.device code that does the real work.
*
* $Id: devio.c,v 1.5 1995/04/08 20:21:52 Rhialto Exp $
* $Log: devio.c,v $
* Revision 1.5 1995/04/08 20:21:52 Rhialto
* Add/correct version strings.
*
* Revision 1.4 1995/04/02 14:58:51 Rhialto
* Work around horrible bug in XPK when compressing non-compressible tracks.
* Allow users to keep old copy of track file when writing the new one fails.
* Provide more realistic values for the disk geometry with TD_GETGEOMETRY.
* Re-initialise more stuff (XPKD:, directories, track cache) on CMD_RESET.
*
* Revision 1.3 1993/11/08 13:11:15 Rhialto
* Add RCS tags.
*
* This code is (C) Copyright 1989-1995 by Olaf Seibert. All rights reserved.
* May not be used or copied without a licence.
-*/
/*
* There is a horrible bug in the xpkmaster.library version 2.4.
* This is the one in Xpk25Usr.lha.
* See below fow the horrid details.
*/
#define XPKBUG 1
#include <string.h>
#include <stdio.h>
#include "xpkdisk.h"
#if NOXPK
#define XPK_MARGIN 0 /* Safety margin for output buffer */
#define XPKERR_OK 0
#define XPKERR_IOERROUT -4 /* Output error happened,look at Result2*/
#else
#define LATTICE /* Get the #pragmas as well */
#include <libraries/xpk.h>
#undef LATTICE
#endif
/*#undef DEBUG */
#if DEBUG
# include "syslog.h"
#else
# define debug(x)
#endif
static const char rcsId[] = "$Id: devio.c,v 1.5 1995/04/08 20:21:52 Rhialto Exp $";
Prototype void UnitSeglist(void);
Prototype void CMD_Read(struct IOStdReq *ioreq, UNIT *unit);
Prototype void CMD_Write(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Format(struct IOStdReq *ioreq, UNIT *unit);
Prototype void CMD_Reset(struct IOStdReq *ioreq, UNIT *unit);
Prototype void CMD_Update(struct IOStdReq *ioreq, UNIT *unit);
Prototype void CMD_Clear(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Seek(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Changenum(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Addchangeint(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Remchangeint(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Getgeometry(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Motor(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Remove(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Protstatus(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Changestate(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Rawread(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Rawwrite(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Getdrivetype(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Getnumtracks(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_Eject(struct IOStdReq *ioreq, UNIT *unit);
Prototype void TD_(struct IOStdReq *ioreq, UNIT *unit);
Prototype int DevInit(DEV *dev);
Prototype int DevCloseDown(DEV *dev);
Prototype UNIT *UnitInit(DEV *dev, ulong UnitNr);
Prototype int UnitCloseDown(struct IOStdReq *ioreq, DEV *dev, UNIT *unit);
struct DosLibrary *DOSBase;
struct Library *XpkBase;
struct IntuitionBase *IntuitionBase;
/* ------------------------------------------------------------------------- */
void *
GetHead(struct MinList *list)
{
if ((void *) list->mlh_Head != (void *) &list->mlh_Tail)
return list->mlh_Head;
return NULL;
}
void *
GetTail(struct MinList *list)
{
if ((void *) list->mlh_Head != (void *) &list->mlh_Tail)
return list->mlh_TailPred;
return NULL;
}
Prototype long min(long a, long b);
long
min(long a, long b)
{
return a < b? a: b;
}
/* ------------------------------------------------------------------------- */
struct CacheTrack {
struct MinNode trk_Node;
short trk_Number;
ushort trk_Refcount;
int trk_Size;
byte trk_Data[0];
};
#define TRK_DIRTY 0x8000 /* Bit in trk_Refcount */
static const char Directory[] = XPKDISKDIR "Unit%x";
int
ReadOnly(UNIT *unit)
{
__aligned struct InfoData infodata;
BPTR fl;
if (fl = Lock(XPKDISKDIR, SHARED_LOCK)) {
if (Info(fl, &infodata)) {
unit->xu_ReadOnly = (infodata.id_DiskState != ID_VALIDATED);
}
UnLock(fl);
}
return unit->xu_ReadOnly;
}
Prototype int MakeDirectory(UNIT *unit);
int
MakeDirectory(UNIT *unit)
{
char dirname[TRACKNAME_LENGTH];
BPTR fl;
sprintf(dirname, Directory, unit->xu_UnitNr);
fl = Lock(dirname, SHARED_LOCK);
if (fl == 0) {
if (fl = CreateDir(dirname)) {
UnLock(fl); /* exclusive locks not invited */
fl = Lock(dirname, SHARED_LOCK);
}
}
debug(("New currentdir: %x\n", fl));
if (fl)
fl = CurrentDir(fl);
debug(("Old currentdir: %x\n", fl));
if (fl)
UnLock(fl);
if (!ReadOnly(unit))
MakeSubDirs(0, unit->xu_NumTracks - 1);
return (int)fl;
}
/* ------------------------------------------------------------------------- */
long
ReadTrack(UNIT *unit, struct CacheTrack *trk)
{
char filename[TRACKNAME_LENGTH];
long res = -1;
#if NOXPK
BPTR fh;
#elif DEBUG
ULONG OutLen;
#endif
#if 0
if (trk->trk_Number >= unit->xu_NumTracks) {
unit->xu_NumTracks = trk->trk_Number + 1;
MakeSubDirs(trk->trk_Number, trk->trk_Number);
}
#endif
NewName(filename, trk->trk_Number);
debug(("ReadTrack: Open %s\n", filename));
#if NOXPK
if (fh = Open(filename, MODE_OLDFILE)) {
debug(("ReadTrack: Open %s succeeded\n", filename));
res = Read(fh, trk->trk_Data, unit->xu_TrackLen);
if (res >= 0 && res < unit->xu_TrackLen) {
memset(trk->trk_Data + res, 0, unit->xu_TrackLen - res);
}
Close(fh);
#else
debug(("Call XpkUnpackTags\n"));
res = XpkUnpackTags(
XPK_OutBuf, trk->trk_Data,
XPK_OutBufLen, unit->xu_TrackLen + XPK_MARGIN,
#if DEBUG
XPK_GetOutLen, &OutLen,
#endif
XPK_InName, filename,
XPK_PassThru, 1L,
TAG_DONE);
#if DEBUG
if (OutLen != unit->xu_TrackLen) {
debug(("XpkUnpackTags: wrong unpacked length: %d\n", OutLen));
}
#endif
if (res == XPKERR_OK) {
#endif
} else {
debug(("ReadTrack: Open %s failed\n", filename));
memset(trk->trk_Data, 0, unit->xu_TrackLen);
}
debug(("res = %d\n", res));
return res;
}
long
WriteFile(char *filename, char *buf, long length, long *ioerr)
{
BPTR fh;
LONG result;
if (fh = Open(filename, MODE_NEWFILE)) {
debug(("WriteFile: Open %s succeded\n", filename));
result = Write(fh, buf, length);
if (result != length) {
result = XPKERR_IOERROUT;
if (ioerr)
*ioerr = IoErr();
} else {
result = XPKERR_OK;
}
Close(fh);
}
return result;
}
long
WriteTrack(UNIT *unit, struct CacheTrack *trk)
{
char filename[TRACKNAME_LENGTH];
char backupname[TRACKNAME_LENGTH + 4];
char errmsg[XPKERRMSGSIZE];
char *errp = errmsg;
#define RETRY 1
#define REVERT 2
#define ABORT 0
int choice = ABORT;
long res = -1;
int ioerr = 0;
if (trk->trk_Number >= unit->xu_NumTracks) {
unit->xu_NumTracks = trk->trk_Number + 1;
MakeSubDirs(trk->trk_Number, trk->trk_Number);
}
if ((unit->xu_CacheFlags & CACHEF_SAFEWRITE) && !CheckRipcord(unit)) {
debug(("Set WriteErr\n"));
unit->xu_WriteErr = TDERR_TooFewSecs;
return -1;
}
NewName(filename, trk->trk_Number);
debug(("WriteTrack: %s\n", filename));
strcpy(backupname, filename);
strcat(backupname, ".old");
Rename(filename, backupname);
/* Keep trying to write the track until we succeed. */
do {
#if !NOXPK
#if !XPKBUG
debug(("Call XpkPackTags %s\n", unit->xu_XPKPackMethod));
res = XpkPackTags(
XPK_InBuf, trk->trk_Data,
XPK_InLen, (long) unit->xu_TrackLen,
XPK_OutName, filename,
XPK_PackMethod, unit->xu_XPKPackMethod,
XPK_StepDown, 1L,
XPK_GetError, &errmsg[0],
TAG_DONE);
#else
struct XpkFH *xfh;
long TrackLen = unit->xu_TrackLen;
char *Data = trk->trk_Data;
long now;
long written;
long save; /* This is the FIX for the UGLY BUG !! */
/*
* Apparently, the master library trashes the longword following
* the input data, if that data is not compressible.
* This is also true when using the NONE compression.
* We can save and restore this longword, because our track
* buffers have extra space at the end for decompressing.
*/
debug(("Call XpkOpenTags %s\n", unit->xu_XPKPackMethod));
res = XpkOpenTags(&xfh,
XPK_OutName, filename,
XPK_PackMethod, unit->xu_XPKPackMethod,
XPK_StepDown, 1L,
XPK_GetError, &errmsg[0],
XPK_InLen, TrackLen,
XPK_ChunkSize, TrackLen,
TAG_DONE);
if (res == XPKERR_OK) {
for (written = 0; written < TrackLen; written += now, Data += now) {
now = min(xfh->NLen, TrackLen - written);
if (now <= 0)
break;
debug(("Call XpkWrite %d\n", now));
save = *(long *)(&Data[now]);
res = XpkWrite(xfh, Data, now);
*(long *)(&Data[now]) = save;
if (res != XPKERR_OK)
break;
}
res = XpkClose(xfh);
}
#endif
if (res == XPKERR_OK)
trk->trk_Refcount &= ~TRK_DIRTY;
else if ((ioerr = IoErr()), res != XPKERR_IOERROUT)
#endif
{
write:
res = WriteFile(filename, trk->trk_Data, unit->xu_TrackLen, &ioerr);
if (res == XPKERR_OK) {
trk->trk_Refcount &= ~TRK_DIRTY;
} else {
errp = "";
}
}
} while (res != XPKERR_OK &&
(choice = FullRetry(unit, filename, res, ioerr, errp)) == RETRY);
if (res == XPKERR_OK) {
DeleteFile(backupname);
} else if (choice == REVERT) {
DeleteFile(filename);
Rename(backupname, filename);
}
#if 0
else if (choice == ABORT && !(trk->trk_Refcount & TRK_DIRTY)) {
DeleteFile(backupname);
}
#endif
debug(("res = %d\n", res));
return res;
}
/*
* Find a specific track. The cache list is a Least Recently Used stack:
* Put it on the head of the cache list. So if it is not used anymore in a
* long time, it bubbles to the end of the list, getting a higher chance
* of being trashed for re-use.
*/
struct CacheTrack *
FindTrackByNumber(UNIT *unit, int number)
{
struct CacheTrack *trk;
struct MinNode *nexttrk;
debug(("FindTrackByNumber %ld\n", (long)number));
trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_Head;
while (nexttrk = trk->trk_Node.mln_Succ) {
if (trk->trk_Number == number) {
debug((" (%lx) %lx\n", (long)trk->trk_Refcount, trk));
Remove((struct Node *)&trk->trk_Node);
AddHead((struct List *)&unit->xu_Cache.LRUList,
(struct Node *)&trk->trk_Node);
return trk;
}
debug(("cache %ld %lx; ", (long)trk->trk_Number, trk));
trk = (struct CacheTrack *)nexttrk;
}
return NULL;
}
/*
* Get a fresh cache buffer. If we are allowed more cache, we just
* allocate memory. Otherwise, we try to find a currently unused buffer.
* We start looking at the end of the list, which is the bottom of the LRU
* stack. If that fails, allocate more memory anyway. Not that is likely
* anyway, since we currently lock only one track at a time.
*/
struct CacheTrack *
NewCacheTrack(UNIT *unit)
{
struct CacheTrack *trk;
struct MinNode *nexttrk;
debug(("NewCacheTrack\n"));
#define SIZE (sizeof(*trk) + unit->xu_TrackLen + XPK_MARGIN)
if (unit->xu_CurrentCache < unit->xu_MaxCache) {
if (trk = AllocMem(SIZE, MEMF_ANY)) {
goto add;
}
}
for (trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_TailPred;
nexttrk = trk->trk_Node.mln_Pred;
trk = (struct CacheTrack *)nexttrk) {
if ((unit->xu_CurrentCache >= unit->xu_MaxCache) &&
(trk->trk_Refcount == TRK_DIRTY)) {
debug(("NewCachetrack: dump dirty trk %d\n", trk->trk_Number));
FreeCacheTrack(unit, trk); /* Also writes it to disk */
continue;
}
if (trk->trk_Refcount == 0) { /* Implies not TRK_DIRTY */
debug(("NewCachetrk: re-use clean trk %d\n", trk->trk_Number));
Remove((struct Node *)&trk->trk_Node);
goto move;
}
}
trk = AllocMem(SIZE, MEMF_ANY);
if (trk) {
add:
trk->trk_Size = SIZE;
unit->xu_CurrentCache++;
move:
AddHead((struct List *) &unit->xu_Cache.LRUList,
(struct Node *) &trk->trk_Node);
}
debug(("NewCacheTrack: %lx\n", trk));
return trk;
#undef SIZE
}
/*
* Dispose a cached track, even if it has a non-zero refcount. If it is
* dirty, write it out.
* If an error occurs, stop.
*/
Prototype int FreeCacheTrack(UNIT *unit, struct CacheTrack *trk);
int
FreeCacheTrack(UNIT *unit, struct CacheTrack *trk)
{
int error;
debug(("FreeCachetrk %ld\n", (long)trk->trk_Number));
if (trk->trk_Refcount & ~TRK_DIRTY) {
debug(("\n\t*** PANIC!!! Refcount not 0 (%x) !!! ***\n\n", trk->trk_Refcount));
trk->trk_Refcount &= TRK_DIRTY;
}
if (trk->trk_Refcount & TRK_DIRTY) {
error = WriteTrack(unit, trk);
} else
error = 0;
if (error == 0) {
Remove((struct Node *)&trk->trk_Node);
FreeMem(trk, trk->trk_Size);
unit->xu_CurrentCache--;
}
return error;
}
/*
* Create an empty cache
*/
void
InitCache(UNIT *unit)
{
NewList((struct List *)&unit->xu_Cache.LRUList);
unit->xu_MaxCache = MAX_CACHE;
unit->xu_CurrentCache = 0;
unit->xu_CacheDirty = 0;
unit->xu_CacheFlags = CACHE_FLAGS;
unit->xu_CacheTimeout = CACHE_TIMEOUT;
}
/*
* Dispose all cached tracks, possibly writing them to disk.
* If an error occurs, stop.
*/
int
FreeCacheList(UNIT *unit)
{
struct CacheTrack *trk;
int error;
debug(("FreeCacheList, %ld\n", (long)unit->xu_CurrentCache));
while ((trk = GetHead(&unit->xu_Cache.LRUList)) &&
(error = FreeCacheTrack(unit, trk)) == 0) {
}
if (error == 0) {
debug(("Clear WriteErr\n"));
unit->xu_WriteErr = 0;
}
return error;
}
struct CacheTrack *
GetTrack(struct IOStdReq *ioreq, int track)
{
UNIT *unit;
struct CacheTrack *trk;
debug(("GetTrack %ld\n", (long)track));
unit = (UNIT *) ioreq->io_Unit;
if (trk = FindTrackByNumber(unit, track)) {
trk->trk_Refcount++;
return trk;
}
if (trk = NewCacheTrack(unit)) {
trk->trk_Refcount = 1;
trk->trk_Number = track;
ReadTrack(unit, trk);
return trk;
}
return NULL;
}
void
MarkTrackDirty(UNIT *unit, struct CacheTrack *trk)
{
if (trk) {
trk->trk_Refcount |= TRK_DIRTY;
unit->xu_CacheDirty = 1;
}
}
/*
* Unlock a cached track. When the usage count drops to zero, which
* implies it is not dirty, and we are over our cache quota, the sector is
* freed. Otherwise we keep it for re-use.
*/
void
FreeTrack(UNIT *unit, struct CacheTrack *trk)
{
if (trk) {
--trk->trk_Refcount;
/*
* If we need to dump cache then dump some long-unused track.
*/
while (unit->xu_CurrentCache > unit->xu_MaxCache &&
(trk = GetTail(&unit->xu_Cache.LRUList)) &&
(trk->trk_Refcount & ~TRK_DIRTY) == 0) {
debug(("Freetrk: dump %s trk %d\n",
(trk->trk_Refcount & TRK_DIRTY)? "dirty" : "clean",
trk->trk_Number));
FreeCacheTrack(unit, trk);
}
}
}
/*
* Decide if we must do an update.
*/
int
Internal_Update(UNIT *unit)
{
struct CacheTrack *trk;
struct MinNode *nexttrk;
int error = 0;
debug(("Internal_Update\n"));
/* Is the cache dirty? */
if (unit->xu_CacheDirty == 0) {
debug(("Cache not dirty\n"));
goto cleartriggers;
}
/* Did we get a CMD_UPDATE, if required? */
if ((unit->xu_CacheFlags & (CACHEF_CMDUPDATE|CACHEF_GOTCMDUPD)) == CACHEF_CMDUPDATE) {
debug(("No required UPDATE\n"));
return 0;
}
/* Did we get a timeout, if required? */
if ((unit->xu_CacheFlags & (CACHEF_DELAY|CACHEF_GOTTIMEOUT)) == CACHEF_DELAY) {
debug(("No required TIMEOUT\n"));
return 0;
}
for (trk = (struct CacheTrack *)unit->xu_Cache.LRUList.mlh_TailPred;
nexttrk = trk->trk_Node.mln_Pred;
trk = (struct CacheTrack *)nexttrk) {
if (trk->trk_Refcount & TRK_DIRTY) {
if (error = WriteTrack(unit, trk)) {
goto end; /* Don't clear update conditions */
}
}
}
unit->xu_CacheDirty = 0;
if (error == 0) {
debug(("Clear WriteErr\n"));
unit->xu_WriteErr = 0;
}
cleartriggers:
unit->xu_CacheFlags &= ~(CACHEF_GOTCMDUPD | CACHEF_GOTTIMEOUT);
end:
return error;
}
/* ------------------------------------------------------------------------- */
/*
* Read zero or more sectors from the disk and copy them into the user's
* buffer.
*/
void
CMD_Read(struct IOStdReq *ioreq, UNIT *unit)
{
int track;
byte *userbuf;
long length;
long offset;
struct CacheTrack *trk;
int error;
debug(("CMD_Read "));
userbuf = (byte *) ioreq->io_Data;
length = ioreq->io_Length;
offset = ioreq->io_Offset;
debug(("userbuf %08lx off %ld len %ld\n", userbuf, offset, length));
track = offset / unit->xu_TrackLen;
offset = offset % unit->xu_TrackLen;
debug(("Tr=%ld Offset=%ld\n", (long)track, (long)offset));
ioreq->io_Actual = length;
error = TDERR_NoError;
if (length <= 0)
goto end;
if (offset != 0) {
/* Handle non-track part first */
ulong l;
trk = GetTrack(ioreq, track);
if (trk == NULL) {
error = TDERR_NoSecHdr;
goto end;
}
l = min(length, unit->xu_TrackLen - offset);
CopyMem(trk->trk_Data + offset, userbuf, l);
userbuf += l;
length -= l;
track++;
FreeTrack(unit, trk);
}
while (length > 0) {
ulong l;
trk = GetTrack(ioreq, track);
if (trk == NULL) {
error = TDERR_NoSecHdr;
goto end;
}
l = min(length, unit->xu_TrackLen);
CopyMem(trk->trk_Data, userbuf, l);
userbuf += l;
length -= l;
track++;
FreeTrack(unit, trk);
}
end:
ioreq->io_Error = error;
if (error != TDERR_NoError)
ioreq->io_Actual = 0;
TermIO(ioreq);
}
void
CMD_Write(struct IOStdReq *ioreq, UNIT *unit)
{
int track;
byte *userbuf;
long length;
long offset;
struct CacheTrack *trk;
int error;
debug(("CMD_Write "));
if (unit->xu_ReadOnly) {
error = TDERR_WriteProt;
goto end;
}
userbuf = (byte *) ioreq->io_Data;
length = ioreq->io_Length;
offset = ioreq->io_Offset;
debug(("userbuf %08lx off %ld len %ld\n", userbuf, offset, length));
track = offset / unit->xu_TrackLen;
offset = offset % unit->xu_TrackLen;
debug(("Tr=%ld Offset=%ld\n", (long)track, (long)offset));
ioreq->io_Actual = length;
error = TDERR_NoError;
if (length <= 0)
goto end;
if (offset != 0) {
/* Handle non-track aligned part first */
ulong l;
trk = GetTrack(ioreq, track);
if (trk == NULL) {
error = TDERR_NoSecHdr;
goto end;
}
l = min(length, unit->xu_TrackLen - offset);
CopyMem(userbuf, trk->trk_Data + offset, l);
userbuf += l;
length -= l;
track++;
MarkTrackDirty(unit, trk);
FreeTrack(unit, trk);
}
while (length > 0) {
ulong l;
trk = GetTrack(ioreq, track);
if (trk == NULL) {
error = TDERR_NoMem;
goto end;
}
l = min(length, unit->xu_TrackLen);
CopyMem(userbuf, trk->trk_Data, l);
userbuf += l;
length -= l;
track++;
MarkTrackDirty(unit, trk);
FreeTrack(unit, trk);
}
end:
if (error == 0 && unit->xu_WriteErr != 0) {
/* Propagate earlier write error(s) of previous tracks */
debug(("Use & Clear WriteErr\n"));
error = unit->xu_WriteErr;
unit->xu_WriteErr = 0;
}
ioreq->io_Error = error;
if (error != TDERR_NoError)
ioreq->io_Actual = 0;
TermIO(ioreq);
StartTimeout(unit);
}
void
TD_Format(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("CMD_Format "));
/* keep it simple for now */
CMD_Write(ioreq, unit);
}
void
CMD_Reset(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("CMD_Reset\n"));
#if 1
/*
* Somebody may have changed XPKD: to point somewhere else,
* so push the cache and then re-establish the current dir.
*/
FreeCacheList(unit); /* Just to be sure */
MakeDirectory(unit);
#endif
MagicInit(unit); /* Get mountlist info and user prefs */
if (!ReadOnly(unit) && (unit->xu_CacheFlags & CACHEF_SAFEWRITE))
MakeRipcord(unit);
TermIO(ioreq);
}
void
CMD_Update(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("CMD_Update\n"));
unit->xu_CacheFlags |= CACHEF_GOTCMDUPD;
StartTimeout(unit);
ioreq->io_Error = Internal_Update(unit);
TermIO(ioreq);
}
void
CMD_Clear(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("CMD_Clear\n"));
ioreq->io_Error = FreeCacheList(unit);
TermIO(ioreq);
}
void
TD_Motor(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("TD_Motor\n"));
ioreq->io_Actual = 1; /* Motor is running */
TermIO(ioreq);
}
void
TD_Return0(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("TD_Seek, TD_Changenum, TD_Changestate\n"));
ioreq->io_Actual = 0;
TermIO(ioreq);
}
void
TD_Protstatus(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("TD_Protstatus\n"));
ioreq->io_Actual = ReadOnly(unit);
TermIO(ioreq);
}
void
TD_Getnumtracks(struct IOStdReq *ioreq, UNIT *unit)
{
debug(("TD_Getnumtracks\n"));
ioreq->io_Actual = unit->xu_NumTracks; /* a guess */
TermIO(ioreq);
}
/*
* Handle disk change interrupts. However, our disks never get taken
* out of their drives.
*/
void
TD_Addchangeint(struct IOStdReq *ioreq, UNIT *unit)
{
Enqueue((struct List *)&unit->xu_ChangeIntList, &ioreq->io_Message.mn_Node);
ioreq->io_Flags &= ~IOF_QUICK; /* So we call ReplyMsg instead of
* TermIO */
/* Note no TermIO */
}
void
TD_Remchangeint(struct IOStdReq *ioreq, UNIT *unit)
{
struct IOStdReq *intreq;
intreq = (struct IOStdReq *) ioreq->io_Data;
Remove(&intreq->io_Message.mn_Node);
ReplyMsg(&intreq->io_Message); /* Quick bit always cleared */
ioreq->io_Error = 0;
TermIO(ioreq);
}
void
TD_Getgeometry(struct IOStdReq *ioreq, UNIT *unit)
{
#if defined(TD_GETGEOMETRY)
struct DriveGeometry *dg;
short numtracks;
debug(("TD_Getgeometry\n"));
dg = (struct DriveGeometry *)ioreq->io_Data;
numtracks = unit->xu_NumTracks;
dg->dg_SectorSize = XD_BPS;
dg->dg_Cylinders = numtracks;
dg->dg_CylSectors = unit->xu_TrackLen / XD_BPS;
dg->dg_TotalSectors = numtracks * dg->dg_CylSectors;
dg->dg_Heads = 1;
dg->dg_TrackSectors = dg->dg_CylSectors;
dg->dg_BufMemType = MEMF_PUBLIC;
dg->dg_DeviceType = DG_DIRECT_ACCESS;
dg->dg_Flags = 0; /* not DGF_REMOVABLE */
#else
ioreq->io_Error = IOERR_NOCMD;
#endif
TermIO(ioreq);
}
/* ------------------------------------------------------------------------- */
#define ROUNDS 2
void
StartTimer(UNIT *unit)
{
WaitIO(&unit->xu_TimeReq.tr_node);
unit->xu_TimeReq.tr_node.io_Command = TR_ADDREQUEST;
unit->xu_TimeReq.tr_time.tv_secs = unit->xu_CacheTimeout / ROUNDS;
unit->xu_TimeReq.tr_time.tv_micro =
(unit->xu_CacheTimeout % ROUNDS) * (1000000 / ROUNDS);
SendIO(&unit->xu_TimeReq.tr_node);
}
Prototype void PollTimer(UNIT *unit);
void
PollTimer(UNIT *unit)
{
if (CheckIO(&unit->xu_TimeReq.tr_node)) {
if (--unit->xu_TimeoutCounter <= 0) {
unit->xu_CacheFlags |= CACHEF_GOTTIMEOUT;
(void)Internal_Update(unit);
} else {
StartTimer(unit);
}
}
}
Prototype void StartTimeout(UNIT *unit);
void
StartTimeout(UNIT *unit)
{
unit->xu_TimeoutCounter = ROUNDS;
if (CheckIO(&unit->xu_TimeReq.tr_node))
StartTimer(unit);
}
int
DevInit(DEV *dev)
{
debug(("DevInit: open dos\n"));
DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 0);
debug(("done DevInit\n"));
return 1; /* Initializing succeeded */
abort:
return DevCloseDown(dev);
}
int
DevCloseDown(dev)
DEV *dev;
{
if (DOSBase) {
CloseLibrary((struct Library *)DOSBase);
DOSBase = NULL;
}
return 0; /* Now unitialized */
}
ulong
InitMsgPort(struct MsgPort *p)
{
p->mp_SigTask = FindTask(NULL);
p->mp_SigBit = AllocSignal(-1);
p->mp_Flags = PA_SIGNAL;
Forbid();
/*
* We must Forbid() here to prevent a race condition. That is also
* sufficient, since interrupts are not allowed to call BeginIO().
*/
if (p->mp_MsgList.lh_Head == NULL)
NewList(&p->mp_MsgList);
Permit();
return 1L << p->mp_SigBit;
}
UNIT *
UnitInit(dev, UnitNr)
DEV *dev;
ulong UnitNr;
{
UNIT *unit;
struct Task *task;
struct MsgPort *p;
unit = AllocMem((long) sizeof (UNIT), MEMF_PUBLIC | MEMF_CLEAR);
if (unit == NULL)
return NULL;
unit->xu_UnitNr = UnitNr;
/*
* Now create the Unit process. Remember that it won't start running
* since we are Forbid()den. But just to be sure, we Forbid() again.
*/
debug(("call CreateProc %x (%x)\n",
MKBADDR(UnitSeglist), UnitSeglist));
Forbid();
p = CreateProc(DevName, TASKPRI, MKBADDR(UnitSeglist), TASKSTACK);
if (p == NULL) {
Permit();
debug(("*** No Unit process!!!\n"));
goto abort;
}
task = (struct Task *)(((char *)p) - offsetof(struct Process, pr_MsgPort));
task->tc_UserData = (APTR) unit;
unit->xu_Port.mp_Flags = PA_IGNORE;
unit->xu_Port.mp_SigTask = task;
NewList(&unit->xu_Port.mp_MsgList);
Permit();
debug(("task: %lx\n", task));
return unit;
abort:
UnitCloseDown(NULL, dev, unit);
return NULL;
}
Prototype ulong UnitInit2(UNIT *unit);
ulong
UnitInit2(UNIT *unit)
{
ulong waitmask;
waitmask = InitMsgPort(&unit->xu_Port);
waitmask |= InitMsgPort(&unit->xu_TimerPort);
unit->xu_TimeReq.tr_node.io_Message.mn_ReplyPort = &unit->xu_TimerPort;
if (OpenDevice("timer.device", UNIT_VBLANK, &unit->xu_TimeReq.tr_node, 0))
return 0;
unit->xu_TimeReq.tr_node.io_Flags = IOF_QUICK;
NewList((struct List *)&unit->xu_ChangeIntList);
unit->xu_TrackLen = XD_TRACKLEN;
strncpy(unit->xu_XPKPackMethod, PACKING_METHOD, 9);
InitCache(unit);
MagicInit(unit); /* Get mountlist info and user prefs */
MakeDirectory(unit);
if (!unit->xu_ReadOnly && (unit->xu_CacheFlags & CACHEF_SAFEWRITE))
MakeRipcord(unit);
#if !NOXPK
XpkBase = OpenLibrary("xpkmaster.library", 0);
if (XpkBase == NULL)
return 0;
#endif
IntuitionBase = OpenLibrary("intuition.library", 33);
if (IntuitionBase == NULL)
return 0;
return waitmask;
}
Prototype void UnitCloseDown2(UNIT *unit);
void
UnitCloseDown2(UNIT *unit)
{
/*
* There may still be dirty tracks due to delayed updates.
* If an error occurs while writing the tracks out, we currently
* lose the memory.
*/
(void)FreeCacheList(unit);
{
struct IORequest *ioreq;
while (ioreq = (struct IORequest *)
RemHead((struct List *)&unit->xu_ChangeIntList)) {
ReplyMsg(&ioreq->io_Message);
}
}
UnLock(CurrentDir(0));
AbortIO(&unit->xu_TimeReq.tr_node);
WaitIO(&unit->xu_TimeReq.tr_node);
CloseDevice(&unit->xu_TimeReq.tr_node);
if (IntuitionBase)
CloseLibrary(IntuitionBase);
if (XpkBase)
CloseLibrary(XpkBase);
}
int
UnitCloseDown(struct IOStdReq *ioreq, DEV *dev, UNIT *unit)
{
/*
* Get rid of the Unit's task. We know this is safe because the unit
* has an open count of zero, so it is 'guaranteed' not in use.
*/
if (unit->xu_Port.mp_SigTask) {
struct IORequest io;
io.io_Message.mn_ReplyPort = (struct MsgPort *)FindTask(NULL);
io.io_Command = CMD_Die;
unit->xu_Flags &= ~UNITF_STOPPED;
debug(("Asking task to die\n"));
PutMsg(&unit->xu_Port, &io.io_Message);
do {
Wait(SIGF_SINGLE);
debug(("Someone exhaled...\n"));
} while (io.io_Message.mn_Node.ln_Type != NT_REPLYMSG);
debug(("That was the last gasp!\n"));
}
FreeMem(unit, (long) sizeof (UNIT));
return 0; /* Now unitialized */
}
/* ------------------------------------------------------------------------- */